﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

    /// <summary>
    /// For every showtime there will be made a list here. This list keep track on which seats are already taken by other customers.
    /// </summary>
    public class ReservedSeatsList
    {
        //This list is mase to make sure of which seats has been picked, to make sure it is possible for a user to select a seat.
        //Make sure the list is atomic!
        #region List of seats

        private static object _lock = new object();

        //This list (it's reference) should never be accessed by anyone else than the methids given in this class!
        private List<Tuple<Seat, DateTime>> _seats;
        private CtrTicket ctt = new CtrTicket();
        private ShowTime sht;

        public ReservedSeatsList(ShowTime showTime)
        {
            _seats = new List<Tuple<Seat, DateTime>>();
            sht = showTime;
            CtrSeat ctseat = new CtrSeat();
            foreach (var seat in ctseat.ReadAll(sht.CinemaRoom.Id))
            {
                if (!(_seats.Exists(x => x.Item1.CinemaRoom == seat.CinemaRoom && x.Item1.Row == seat.Row && x.Item1.SeatNr == seat.SeatNr)))
                {
                    AddSeat(seat);
                }
            }
            PopulateSeats();
        }

        public void AddSeat(Seat seat)
        {
            Monitor.Enter(_lock);
            try
            {
                if (!_seats.Exists(x => x.Item1.SeatNr == seat.SeatNr && x.Item1.Row == seat.Row && x.Item1.CinemaRoom == seat.CinemaRoom) 
                    && sht.CinemaRoom.Id == seat.CinemaRoom)
                {
                    _seats.Add(new Tuple<Seat, DateTime>(seat, DateTime.MinValue));
                }
            }
            finally
            {
                Monitor.Exit(_lock);
            }
        }

        public void SelectSeat(Seat seat)
        {
            Monitor.Enter(_lock);
            try
            {
                
                _seats.Remove(_seats.Single(x =>
                                            x.Item1.SeatNr == seat.SeatNr
                                            && x.Item1.Row == seat.Row
                                            && x.Item1.CinemaRoom == seat.CinemaRoom
                                            && sht.CinemaRoom.Id == seat.CinemaRoom
                                    ));
                seat.IsTaken = true;
                _seats.Add(new Tuple<Seat, DateTime>(seat, DateTime.Now));
            }
            finally
            {
                Monitor.Exit(_lock);
            }
        }

        public List<Seat> GetSeatList()
        {
            //If someone forgot to remove their seat, we need to make sure it's available again.
            CheckTimeStamp();
            //We always want the list to be updated!
            PopulateSeats();
            //Make sure to return a new list instead of the reference to the original.
            List<Seat> retList = _seats.Select(x => x.Item1).OrderBy(x => x.Row).ThenBy(x => x.SeatNr).ToList();
            return retList;
        }

        public void RemoveSeat(Seat seat)
        {
            Monitor.Enter(_lock);
            try
            {
                _seats.Remove(_seats.Single(x =>
                                            x.Item1.SeatNr == seat.SeatNr
                                            && x.Item1.Row == seat.Row
                                            && x.Item1.CinemaRoom == seat.CinemaRoom
                                            && sht.CinemaRoom.Id == seat.CinemaRoom
                                    ));
                AddSeat(seat);
            }
            finally
            {
                Monitor.Exit(_lock);
            }
        }

        public void CheckTimeStamp()
        {
            Monitor.Enter(_lock);
            try
            {
                //How much time should people have to book their seats.
                foreach (var tuple in _seats.FindAll(x => x.Item1.IsTaken && DateTime.Now.Subtract(x.Item2).Minutes > 5))
                {
                    tuple.Item1.IsTaken = false;
                }
                
            }
            finally
            {
                Monitor.Exit(_lock);
            }
        }

        public void PopulateSeats()
        {

            Monitor.Enter(_lock);
            try
            {
                List<Ticket> tickets = ctt.Read(sht);

                foreach (var ticket in tickets)
                {
                    foreach (var tuple in _seats.Where(x => x.Item1.SeatNr == ticket.Seat.SeatNr 
                                                            && x.Item1.Row == ticket.Seat.Row 
                                                            && x.Item1.CinemaRoom == ticket.Seat.CinemaRoom 
                                                            && !ticket.Invalid))
                    {
                        tuple.Item1.IsTaken = true;
                        //these seats are permanent.
                        tuple.Item2.Add(DateTime.MaxValue - DateTime.MinValue);
                    }
                }
                
            }
            catch (Exception e)
            {
            }
            finally
            {
                Monitor.Exit(_lock);
            }
        }

        #endregion
    }

